package io.itit.ecp.admin.provide.action.controller;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import icu.mhb.mybatisplus.plugln.core.JoinLambdaWrapper;
import icu.mhb.mybatisplus.plugln.extend.Joins;
import io.itit.ecp.admin.common.enums.IMeterDeviceEnums;
import io.itit.ecp.admin.provide.action.req.MeterDeviceQueryReq;
import io.itit.ecp.admin.provide.action.req.MeterDeviceReq;
import io.itit.ecp.admin.provide.action.resp.MeterDeviceResp;
import io.itit.ecp.admin.provide.action.resp.PhysicalModelResp;
import io.itit.ecp.admin.provide.filter.IMeterDeviceFilter;
import io.itit.ecp.admin.provide.helper.SceneHelper;
import io.itit.ecp.admin.provide.renderer.MeterDeviceRenderer;
import io.itit.ecp.admin.server.entity.*;
import io.itit.ecp.admin.server.service.*;
import io.itit.grass.common.exception.GrassException;
import io.itit.grass.common.provide.action.req.BaseBatchIdReq;
import io.itit.grass.common.provide.action.req.BaseIdReq;
import io.itit.grass.common.provide.action.resp.ResultResp;
import io.itit.grass.common.provide.action.validation.*;
import io.itit.grass.common.server.helper.DictHelper;
import io.itit.grass.common.server.utils.ConvertUtils;
import io.itit.grass.estelle.starter.core.ExcelXSSFUtil;
import io.itit.grass.estelle.starter.parse.ExcelParseManage;
import io.itit.grass.estelle.starter.util.JsonUtil;
import io.itit.grass.web.wall.utils.PageUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 *
 * @Author：Roy
 * @Description：计量设备管理控制类
 * @Date：Create by 2023-11-30 18:51:39
 */
@Api(tags = "计量设备管理")
@RestController
@RequestMapping("/ecp-admin/meterDevice")
public class MeterDeviceController {

    @Resource
    private IMeterDeviceService meterDeviceService;

    @Resource
    private ICarbonEmissionFactorService carbonEmissionFactorService;

    @Resource
    private IEmissionReductionFactorService emissionReductionFactorService;

    @Resource
    private IEnergyConsumptionTypeService energyConsumptionTypeService;

    @Resource
    private IDeviceService deviceService;

    @Resource
    private IProductTypeService productTypeService;

    @Resource
    private IPhysicalModelService physicalModelService;

    @Resource
    private IEnergyDataService energyDataService;

    @ApiOperation("分页查询")
    @PostMapping("queryPage")
    @RequiresPermissions("ecp-admin:meterDevice:search")
    public ResultResp<IPage<MeterDeviceResp>> queryPage(@RequestBody @Validated(IQueryPage.class) MeterDeviceQueryReq req) {
        Page<MeterDeviceResp> page = buildQW(req).joinPage(PageUtils.getPage(req, MeterDeviceResp.class), MeterDeviceResp.class);
        return ResultResp.success(SceneHelper.i().setSceneInfo(page));
    }

    @ApiOperation("详情")
    @PostMapping("detail")
    public ResultResp<MeterDeviceResp> detail(@RequestBody @Validated(IRequired.class) BaseIdReq req) {
        return ResultResp.success(ConvertUtils.copy(meterDeviceService.getById(req.getId()), MeterDeviceResp.class));
    }

    @ApiOperation("详情")
    @GetMapping("detailByCode")
    public ResultResp<MeterDeviceResp> detailByCode(@RequestParam String deviceCode) {
        MeterDeviceResp deviceResp = ConvertUtils.copy(meterDeviceService.findByDeviceCode(deviceCode), MeterDeviceResp.class);
        if (Objects.nonNull(deviceResp) && StringUtils.isNotEmpty(deviceResp.getEnergyTypeId())) {
            EnergyConsumptionTypeEntity energyConsumptionType = energyConsumptionTypeService.getById(deviceResp.getEnergyTypeId());
            if (Objects.nonNull(energyConsumptionType)) {
                deviceResp.setEnergyTypeUnit(energyConsumptionType.getEnergyUnit());
                deviceResp.setCalculationRange(energyConsumptionType.getRangeId());
                deviceResp.setEnergyCategory(energyConsumptionType.getEnergyCategory());
            }
        }
        return ResultResp.success(deviceResp);
    }

    @ApiOperation("详情")
    @PostMapping("deviceModel")
    @RequiresPermissions("ecp-admin:meterDevice:search")
    public ResultResp<List<PhysicalModelResp>> findDeviceModels(@RequestParam String deviceId) {
        DeviceEntity deviceEntity = deviceService.getById(deviceId);
        if (Objects.isNull(deviceEntity)) {
            return ResultResp.fail("设备信息不存在");
        }
        ProductTypeEntity productTypeEntity = productTypeService.getById(deviceEntity.getProductTypeId());
        if (Objects.isNull(productTypeEntity)) {
            return ResultResp.fail("设备关联产品信息不存在");
        }
        String modelIds = productTypeEntity.getModelIds();
        if (StringUtils.isNotEmpty(modelIds)) {
            String[] modelIdArr = modelIds.split(",");
            List<PhysicalModelEntity> physicalModelEntities = physicalModelService.listByIds(Arrays.asList(modelIdArr));
            return ResultResp.success(ConvertUtils.convert(physicalModelEntities, PhysicalModelResp.class));
        }
        return ResultResp.success(Collections.emptyList());
    }

    /**
     * 组装查询条件
     */
    private static JoinLambdaWrapper<MeterDeviceEntity> buildQW(MeterDeviceQueryReq req) {
        return Joins.of(MeterDeviceEntity.class)
                .leftJoin(SceneProjectEntity.class, SceneProjectEntity::getId, MeterDeviceEntity::getSceneProjectId)
                .select(SceneProjectEntity::getProjectName)
                .end()
                .leftJoin(ProductTypeEntity.class, ProductTypeEntity::getId, MeterDeviceEntity::getProductTypeId)
                .selectAs(ProductTypeEntity::getProductTypeName, MeterDeviceResp::getProductTypeName)
                .end()
                .leftJoin(DeviceEntity.class, DeviceEntity::getId, MeterDeviceEntity::getDeviceId)
                .selectAs(builder -> builder.addFunAlias(DeviceEntity::getDeviceType, MeterDeviceResp::getDeviceParamType)
                        .addFunAlias(DeviceEntity::getSceneRoomId, MeterDeviceResp::getDeviceParamSceneRoomId))
                .end()
                .leftJoin(EnergyConsumptionTypeEntity.class, EnergyConsumptionTypeEntity::getId, MeterDeviceEntity::getEnergyTypeId)
                .selectAs(EnergyConsumptionTypeEntity::getEnergyName, MeterDeviceResp::getEnergyTypeName)
                .end()
                .leftJoin(PhysicalModelEntity.class, PhysicalModelEntity::getId, MeterDeviceEntity::getModelId)
                .selectAs(PhysicalModelEntity::getModelName, MeterDeviceResp::getModelName)
                .end()
                .like(StringUtils.isNotEmpty(req.getDeviceCode()), MeterDeviceEntity::getDeviceCode, req.getDeviceCode())
                .like(StringUtils.isNotEmpty(req.getDeviceName()), MeterDeviceEntity::getDeviceName, req.getDeviceName())
                .and(StringUtils.isNotEmpty(req.getDeviceCodeOrName()), t ->
                        t.like(MeterDeviceEntity::getDeviceCode, req.getDeviceCodeOrName())
                                .or()
                                .like(MeterDeviceEntity::getDeviceName, req.getDeviceCodeOrName()))
                .eq(StringUtils.isNotEmpty(req.getDeviceType()), MeterDeviceEntity::getDeviceType, req.getDeviceType())
                .in(CollectionUtils.isNotEmpty(req.getDeviceTypeList()), MeterDeviceEntity::getDeviceType, req.getDeviceTypeList())
                .eq(StringUtils.isNotEmpty(req.getDeviceStatus()), MeterDeviceEntity::getDeviceStatus, req.getDeviceStatus())
                .in(CollectionUtils.isNotEmpty(req.getDeviceStatusList()), MeterDeviceEntity::getDeviceStatus, req.getDeviceStatusList())
                .eq(StringUtils.isNotEmpty(req.getMeterType()), MeterDeviceEntity::getMeterType, req.getMeterType())
                .in(CollectionUtils.isNotEmpty(req.getMeterTypeList()), MeterDeviceEntity::getMeterType, req.getMeterTypeList())
                .eq(StringUtils.isNotEmpty(req.getSceneProjectId()), MeterDeviceEntity::getSceneProjectId, req.getSceneProjectId())
                .eq(StringUtils.isNotEmpty(req.getSceneEdificeId()), MeterDeviceEntity::getSceneEdificeId, req.getSceneEdificeId())
                .eq(StringUtils.isNotEmpty(req.getSceneFloorId()), MeterDeviceEntity::getSceneFloorId, req.getSceneFloorId())
                .eq(StringUtils.isNotEmpty(req.getSceneRoomId()), MeterDeviceEntity::getSceneRoomId, req.getSceneRoomId())
                .eq(StringUtils.isNotEmpty(req.getReadMeterType()), MeterDeviceEntity::getReadMeterType, req.getReadMeterType())
                .in(CollectionUtils.isNotEmpty(req.getReadMeterTypeList()), MeterDeviceEntity::getReadMeterType, req.getReadMeterTypeList())
                .eq(StringUtils.isNotEmpty(req.getEnergyTypeId()), MeterDeviceEntity::getEnergyTypeId, req.getEnergyTypeId())
                .eq(StringUtils.isNotEmpty(req.getReadMeterTag()), MeterDeviceEntity::getReadMeterTag, req.getReadMeterTag())
                .in(CollectionUtils.isNotEmpty(req.getReadMeterTagList()), MeterDeviceEntity::getReadMeterTag, req.getReadMeterTagList())
                .eq(StringUtils.isNotEmpty(req.getPurpose()), MeterDeviceEntity::getPurpose, req.getPurpose())
//				.in(CollectionUtils.isNotEmpty(req.getPurposeList()), MeterDeviceEntity::getPurpose, req.getPurposeList())
                .eq(StringUtils.isNotEmpty(req.getProductTypeId()), MeterDeviceEntity::getProductTypeId, req.getProductTypeId())
                .eq(StringUtils.isNotEmpty(req.getStatus()), MeterDeviceEntity::getStatus, req.getStatus())
                .in(CollectionUtils.isNotEmpty(req.getStatusList()), MeterDeviceEntity::getStatus, req.getStatusList())
                .eq(StringUtils.isNotEmpty(req.getDelFlag()), MeterDeviceEntity::getDelFlag, req.getDelFlag())
                .in(CollectionUtils.isNotEmpty(req.getDelFlagList()), MeterDeviceEntity::getDelFlag, req.getDelFlagList())
                .eq(StringUtils.isNotEmpty(req.getDeptId()), MeterDeviceEntity::getDeptId, req.getDeptId())
                .in(CollectionUtils.isNotEmpty(req.getIds()), MeterDeviceEntity::getId, req.getIds())
                .last(StringUtils.isNotEmpty(req.getSortColumn()), PageUtils.lastOrderBy(req))
                .orderByDesc(StringUtils.isEmpty(req.getSortColumn()), MeterDeviceEntity::getCreateTm);
    }

    @ApiOperation("新增")
    @PostMapping("add")
    @RequiresPermissions("ecp-admin:meterDevice:add")
    public ResultResp<Boolean> add(@RequestBody @Validated({IAdd.class}) MeterDeviceReq req) {
        checkDataExists(req);
        return ResultResp.success(meterDeviceService.saveOrUpdate(ConvertUtils.convert(req, MeterDeviceEntity.class)));
    }

    @ApiOperation("编辑")
    @PostMapping("update")
    @RequiresPermissions("ecp-admin:meterDevice:update")
    public ResultResp<Boolean> update(@RequestBody @Validated({IUpdate.class}) MeterDeviceReq req) {
        checkDataExists(req);
        boolean res = meterDeviceService.updateById(ConvertUtils.copy(req, MeterDeviceEntity.class));
        if (res) {
            energyDataService.lambdaUpdate()
                    .set(EnergyDataEntity::getMeterDeviceId, req.getId())
                    .eq(EnergyDataEntity::getDeviceCode, req.getDeviceCode())
                    .update();
        }
        return ResultResp.success(res);
    }

    private void checkDataExists(MeterDeviceReq req) {
        Long count = meterDeviceService.lambdaQuery()
                .eq(MeterDeviceEntity::getDeviceCode, req.getDeviceCode())
                .eq(MeterDeviceEntity::getDelFlag, IMeterDeviceEnums.DelFlag.delFlag1.delFlag)
                .ne(StringUtils.isNotEmpty(req.getId()), MeterDeviceEntity::getId, req.getId())
                .count();
        GrassException.throwEx(count > 0, "设备编码已存在！");
    }

    @ApiOperation("启用/禁用")
    @PostMapping("enableOrDisable")
    @RequiresPermissions("ecp-admin:meterDevice:enableOrDisable")
    public ResultResp<Boolean> enableOrDisable(@RequestBody @Validated({IEnable.class}) MeterDeviceReq req) {
        DictHelper.i().checkV(IMeterDeviceFilter.METER_DEVICE_STATUS, req.getStatus());
        return ResultResp.success(meterDeviceService.update(Wrappers.<MeterDeviceEntity>lambdaUpdate()
                .set(MeterDeviceEntity::getStatus, req.getStatus())
                .eq(MeterDeviceEntity::getId, req.getId())));
    }

    @ApiOperation("删除(根据Id删)")
    @PostMapping("deleteById")
    @RequiresPermissions("ecp-admin:meterDevice:deleteById")
    public ResultResp<Boolean> deleteById(@RequestBody @Validated({IRequired.class}) BaseIdReq req) {
        return ResultResp.success(meterDeviceService.deleteMeterDevice(Collections.singletonList(req.getId())));
    }

    @ApiOperation("批量删除")
    @PostMapping("batchDelete")
    @RequiresPermissions("ecp-admin:meterDevice:batchDelete")
    public ResultResp<Boolean> batchDelete(@RequestBody @Validated({IRequired.class}) BaseBatchIdReq req) {
        return ResultResp.success(meterDeviceService.deleteMeterDevice(req.getIds()));
    }

    @ApiOperation("导入")
    @PostMapping("upload")
    @RequiresPermissions("ecp-admin:meterDevice:upload")
    public ResultResp<Integer> upload(@RequestParam("file") MultipartFile file, Boolean ignoreError) {
        return ResultResp.success(ExcelParseManage.i().process(MeterDeviceRenderer.ENTITY_NAME, file, ignoreError));
    }

    @ApiOperation("导出")
    @PostMapping("export")
    @RequiresPermissions("ecp-admin:meterDevice:export")
    public void export(@RequestBody @Validated(IExport.class) MeterDeviceQueryReq req, HttpServletResponse response) {
        ExcelXSSFUtil.export("MeterDeviceExport", JsonUtil.toJson(meterDeviceService.list(buildQW(req)), new IMeterDeviceFilter.MeterDeviceExportFilter()), response);
    }

    @ApiOperation("根据项目Id获取计量设备")
    @PostMapping("queryMeterDeviceByProjectId")
    public ResultResp<List<MeterDeviceResp>> queryMeterDeviceByProjectId(@RequestBody @Validated(IQueryPage.class) MeterDeviceQueryReq req) {
        return ResultResp.success(buildQW(req).joinList(MeterDeviceResp.class));
    }

    @ApiOperation("计量设备查询能源类型关联了减排因子的计量设备")
    @PostMapping("queryMeterDeviceWithEmissionReductionFactor")
    public ResultResp<List<MeterDeviceResp>> queryMeterDeviceWithEmissionReductionFactor(@RequestBody MeterDeviceQueryReq req) {
        List<EmissionReductionFactorEntity> factorEntities = emissionReductionFactorService.list(new LambdaQueryWrapper<>());
        Set<String> energyTypeIds = factorEntities.stream()
                .map(EmissionReductionFactorEntity::getEnergyTypeId).collect(Collectors.toSet());
        if (CollectionUtils.isEmpty(energyTypeIds)) {
            return ResultResp.success(Collections.emptyList());
        }
        List<MeterDeviceEntity> list = meterDeviceService.list(new LambdaQueryWrapper<MeterDeviceEntity>()
                .eq(StringUtils.isNotEmpty(req.getSceneProjectId()), MeterDeviceEntity::getSceneProjectId, req.getSceneProjectId())
                .in(StringUtils.isNotEmpty(req.getSceneEdificeId()), MeterDeviceEntity::getSceneEdificeId, Arrays.asList(StringUtils.split(req.getSceneEdificeId(), ",")))
                .in(StringUtils.isNotEmpty(req.getSceneFloorId()), MeterDeviceEntity::getSceneFloorId, Arrays.asList(StringUtils.split(req.getSceneFloorId(), ",")))
                .in(StringUtils.isNotEmpty(req.getSceneRoomId()), MeterDeviceEntity::getSceneRoomId, Arrays.asList(StringUtils.split(req.getSceneRoomId(), ",")))
                .in(MeterDeviceEntity::getEnergyTypeId, energyTypeIds));
        return ResultResp.success(ConvertUtils.convert(list, MeterDeviceResp.class));
    }

    @ApiOperation("计量设备查询能源类型关联了碳排因子的计量设备")
    @PostMapping("queryMeterDeviceWithEmissionFactor")
    public ResultResp<List<MeterDeviceResp>> queryMeterDeviceWithEmissionFactor(@RequestBody MeterDeviceQueryReq req) {
        List<CarbonEmissionFactorEntity> factorEntities = carbonEmissionFactorService.list(new LambdaQueryWrapper<>());
        Set<String> energyTypeIds = factorEntities.stream().map(CarbonEmissionFactorEntity::getEnergyTypeId).collect(Collectors.toSet());
        if (CollectionUtils.isEmpty(energyTypeIds)) {
            return ResultResp.success(Collections.emptyList());
        }
        List<MeterDeviceEntity> list = meterDeviceService.list(new LambdaQueryWrapper<MeterDeviceEntity>()
                .eq(StringUtils.isNotEmpty(req.getSceneProjectId()), MeterDeviceEntity::getSceneProjectId, req.getSceneProjectId())
                .in(StringUtils.isNotEmpty(req.getSceneEdificeId()), MeterDeviceEntity::getSceneEdificeId, Arrays.asList(StringUtils.split(req.getSceneEdificeId(), ",")))
                .in(StringUtils.isNotEmpty(req.getSceneFloorId()), MeterDeviceEntity::getSceneFloorId, Arrays.asList(StringUtils.split(req.getSceneFloorId(), ",")))
                .in(StringUtils.isNotEmpty(req.getSceneRoomId()), MeterDeviceEntity::getSceneRoomId, Arrays.asList(StringUtils.split(req.getSceneRoomId(), ",")))
                .in(MeterDeviceEntity::getEnergyTypeId, energyTypeIds));
        return ResultResp.success(ConvertUtils.convert(list, MeterDeviceResp.class));
    }
}
